#include "Pch.h"

extern "C" {
	#include "Matt/MultipleAlignment.h"
}

#include "AlignmentDialog.h"
#include "FileRequest.h"
#include "Config.h"
#include <ctype.h>

class NiceList : public wxListCtrl {
public:
	void OnEraseBackground(wxEraseEvent & evt);
	NiceList(wxWindow *parent, int id=wxID_ANY) :
		wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT)
	{
	}

	DECLARE_EVENT_TABLE()
};

void NiceList::OnEraseBackground(wxEraseEvent & evt) {
   // to prevent flickering, erase only content *outside* of the
   // actual list items stuff
   if(GetItemCount() > 0) {
       wxDC * dc = evt.GetDC();

       // get some info
       wxCoord width = 0, height = 0;
       GetClientSize(&width, &height);

       wxCoord x, y, w, h;
       dc->SetClippingRegion(0, 0, width, height);
       dc->GetClippingBox(&x, &y, &w, &h);

       long top_item = GetTopItem();
       long bottom_item = top_item + GetCountPerPage();
       if(bottom_item >= GetItemCount()) {
           bottom_item = GetItemCount() - 1;
       }

       // trick: we want to exclude a couple pixels
       // on the left side thus use wxLIST_RECT_LABEL
       // for the top rect and wxLIST_RECT_BOUNDS for bottom
       // rect
       wxRect top_rect, bottom_rect;
       GetItemRect(top_item, top_rect, wxLIST_RECT_LABEL);
       GetItemRect(bottom_item, bottom_rect, wxLIST_RECT_BOUNDS);

       // set the new clipping region and do erasing
       wxRect items_rect(top_rect.GetLeftTop(), bottom_rect.GetBottomRight());
       wxRegion reg(wxRegion(x, y, w, h));
       reg.Subtract(items_rect);
       dc->DestroyClippingRegion();
       dc->SetClippingRegion(reg);

       // do erasing
       dc->SetBackground(wxBrush(GetBackgroundColour(), wxSOLID));
       dc->Clear();

       // restore old clipping region
       dc->DestroyClippingRegion();
       dc->SetClippingRegion(wxRegion(x, y, w, h));
   } else {
       evt.Skip();
   }
}

BEGIN_EVENT_TABLE(NiceList, wxListCtrl)
	EVT_ERASE_BACKGROUND(NiceList::OnEraseBackground)
END_EVENT_TABLE()

PDBInfo::PDBInfo() {
	chain = 0;
}

PDBInfo::~PDBInfo() {
}

AlignmentInfo::AlignmentInfo() {
	pdb = 0;
	numPDB = 0;
	flags = config->alignmentFlags;
}

AlignmentInfo::~AlignmentInfo() {
	for (int i=0; i<numPDB; i++) {
		delete pdb[i];
	}
	free(pdb);
}

wxSizer *CreateFileSizer(wxFilePickerCtrl *&w, wxWindow *parent, int id, const wxString &prompt, const wxString &path=wxEmptyString) {
	wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);
	sizer->Add(new wxStaticText(parent, wxID_ANY, prompt), 0, wxALIGN_CENTER_VERTICAL);
	sizer->Add(6, 0);
	w = new wxFilePickerCtrl(parent, id, path, prompt, _T("PDB files (*.pdb;*.ent)|*.pdb;*.pdb.gz;*.pdb.Z;*.ent;*.ent.gz;*.ent.Z|All files (*.*)|*"), wxDefaultPosition, wxDefaultSize, (wxFLP_DEFAULT_STYLE &~ wxFLP_FILE_MUST_EXIST) | wxFLP_USE_TEXTCTRL);
	// For whatever reason, have to do it this way, unless I hard code height (Using -1 doesn't work).
	wxSize s = w->GetEffectiveMinSize();
	s.SetWidth(350);
	w->SetInitialSize(s);
	sizer->Add(w, 1, wxEXPAND);
	return sizer;
}

wxSizer *CreateShortTextSizer(wxTextCtrl *&textCtrl, wxWindow *parent, int id, const wxString &prompt, const wxString &text=wxEmptyString) {
	wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);
	sizer->Add(new wxStaticText(parent, wxID_ANY, prompt), 0, wxALIGN_CENTER_VERTICAL);
	sizer->Add(6, 0);
	textCtrl = new wxTextCtrl(parent, id, text, wxDefaultPosition, wxSize(80, -1));
	textCtrl->SetMaxLength(512);
	sizer->Add(textCtrl, 1, wxEXPAND);
	return sizer;
}

enum AlignmentDialogId {
	ID_ALIGNMENT_PATH,
	ID_ALIGNMENT_CHAIN,
	ID_ALIGNMENT_MODEL,

	ID_ALIGNMENT_ADD_CHAIN,

	ID_ALIGNMENT_CHAIN_LIST,

	ID_ALIGNMENT_REMOVE_CHAIN,
	ID_ALIGNMENT_CLEAR,
	ID_ALIGNMENT_ALIGN,

	ID_ALIGNMENT_DOWNLOAD_CANCEL,
	ID_ALIGNMENT_DOWNLOAD,
	ID_ALIGNMENT_DOWNLOAD_AND_ADD,
};

class AlignmentDialog : public wxDialog {
public:
	int numRequests;
	FileRequest ** requests;
	int lastRequestIsAdd;

	PDBData *currentPDB;
	AlignmentInfo *info;
	int creating;
	wxString path;
	wxListCtrl *list;
	wxFilePickerCtrl *file;
	wxButton *add, *align, *downloadCancel, *downloadAndAdd, *download;
	wxTextCtrl *model, *chain;

	wxCheckBox *cbNewWindow;
	wxRadioButton *rbSingleWindow, *rbMultipleWindows, *rbMultipleFrames;

	wxTimer RequestTimer;

	AlignmentDialog(const wxString &path) {
		RequestTimer.SetOwner(this);
		this->path = path;
		currentPDB = 0;
		info = new AlignmentInfo();
		creating = 0;
		list = 0;

		numRequests = 0;
		lastRequestIsAdd = 1;
		requests = 0;
	}

	~AlignmentDialog() {
		CleanupRequests();

		if (info) delete info;
		if (currentPDB) CleanupPDB(currentPDB);
	}

	bool Create(wxWindow *parent, int forceNoNewWindow) {
		creating = 1;
		if (!wxDialog::Create(parent, wxID_ANY, _T("Run Alignment"), wxDefaultPosition, wxDefaultSize, (wxDEFAULT_DIALOG_STYLE & ~wxBORDER_MASK) | wxRESIZE_BORDER)) return 0;

		wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);

		vsizer->AddSpacer(2);
		{
			wxStaticBoxSizer *chainSizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _T("Add Chains"));
			chainSizer->AddSpacer(8);

			wxBoxSizer *chainSizer2 = new wxBoxSizer(wxVERTICAL);
			chainSizer2->AddSpacer(8);
			chainSizer2->Add(CreateFileSizer(file, this, ID_ALIGNMENT_PATH, _T("Path"), path), 0, wxEXPAND);
			chainSizer2->AddSpacer(8);

			wxSizer *hsizer = new wxBoxSizer(wxHORIZONTAL);
			hsizer->Add(CreateShortTextSizer(model, this, ID_ALIGNMENT_MODEL, _T("Model"), _T("")));
			hsizer->AddSpacer(6);
			hsizer->Add(CreateShortTextSizer(chain, this, ID_ALIGNMENT_CHAIN, _T("Chain(s)"), _T("")));
			hsizer->AddSpacer(20);
			hsizer->Add(add = new wxButton(this, ID_ALIGNMENT_ADD_CHAIN, _T("Add")));

			chainSizer2->Add(hsizer);
			chainSizer2->AddSpacer(10);

			hsizer = new wxBoxSizer(wxHORIZONTAL);
			hsizer->Add(downloadCancel = new wxButton(this, ID_ALIGNMENT_DOWNLOAD_CANCEL, _T("Cancel Downloads")));
			hsizer->AddSpacer(6);
			hsizer->Add(download = new wxButton(this, ID_ALIGNMENT_DOWNLOAD, _T("Download")));
			hsizer->AddSpacer(6);
			hsizer->Add(downloadAndAdd = new wxButton(this, ID_ALIGNMENT_DOWNLOAD_AND_ADD, _T("Download and Add")));

			chainSizer2->Add(hsizer, 0, wxALIGN_RIGHT);
			chainSizer2->AddSpacer(4);

			chainSizer->Add(chainSizer2);
			chainSizer->AddSpacer(6);

			vsizer->Add(chainSizer, 0, wxEXPAND);
		}

		vsizer->AddSpacer(6);

		{
			list = new NiceList(this, ID_ALIGNMENT_CHAIN_LIST);
			list->InsertColumn(0, _T("File"), wxLIST_FORMAT_RIGHT, 150);
			list->InsertColumn(1, _T("Model"), wxLIST_FORMAT_LEFT, 60);
			list->InsertColumn(2, _T("Chain"), wxLIST_FORMAT_LEFT, 60);
			list->InsertColumn(3, _T("Residues"), wxLIST_FORMAT_LEFT, 100);

			list->SetMinSize(wxSize(100, 300));
			vsizer->Add(list, 1, wxEXPAND);
		}
		vsizer->AddSpacer(6);

		{
			wxSizer *hsizer = new wxBoxSizer(wxHORIZONTAL);
			hsizer->AddSpacer(2);
			hsizer->Add(new wxButton(this, ID_ALIGNMENT_REMOVE_CHAIN, _T("Delete")));
			hsizer->AddSpacer(10);
			hsizer->Add(new wxButton(this, ID_ALIGNMENT_CLEAR, _T("Clear")));
			vsizer->Add(hsizer);
		}
		vsizer->AddSpacer(6);
		{

			wxStaticBoxSizer *chainSizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _T("Windowing Option"));
			chainSizer->AddSpacer(8);

			wxBoxSizer *chainSizer2 = new wxBoxSizer(wxVERTICAL);
			chainSizer2->AddSpacer(6);

			int alignmentFlags = config->alignmentFlags;
			if (!forceNoNewWindow) {
				chainSizer2->Add(cbNewWindow = new wxCheckBox(this, wxID_ANY, _T("Open in new window")));
				if (alignmentFlags & ALIGNMENT_NEW_WINDOW) cbNewWindow->SetValue(1);
				chainSizer2->AddSpacer(6);
			}
			else {
				cbNewWindow = 0;
			}

			chainSizer2->Add(rbSingleWindow = new wxRadioButton(this, wxID_ANY, _T("Use single window"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP));
			chainSizer2->AddSpacer(2);
			chainSizer2->Add(rbMultipleWindows = new wxRadioButton(this, wxID_ANY, _T("Use multiple windows")));
			chainSizer2->AddSpacer(2);
			chainSizer2->Add(rbMultipleFrames = new wxRadioButton(this, wxID_ANY, _T("Use multiple frames in one window")));

			if (alignmentFlags & ALIGNMENT_MULTIPLE_WINDOWS) rbMultipleWindows->SetValue(1);
			else if (alignmentFlags & ALIGNMENT_MULTIPLE_FRAMES) rbMultipleFrames->SetValue(1);
			else rbSingleWindow->SetValue(1);


			chainSizer2->AddSpacer(6);

			chainSizer->Add(chainSizer2);
			chainSizer->AddSpacer(8);

			vsizer->Add(chainSizer, 0, wxEXPAND);
		}
		vsizer->AddSpacer(6);
		{
			wxSizer *hsizer = new wxBoxSizer(wxHORIZONTAL);
			hsizer->Add(align = new wxButton(this, ID_ALIGNMENT_ALIGN, _T("Align")));
			hsizer->AddSpacer(10);
			hsizer->Add(new wxButton(this, wxID_CANCEL, _T("Cancel")));
			hsizer->AddSpacer(2);
			vsizer->Add(hsizer, 0, wxALIGN_RIGHT);
		}
		vsizer->AddSpacer(4);

		//wxBoxSizer *rootsizer = new wxBoxSizer(wxHORIZONTAL);
		//rootsizer->AddSpacer(2);
		//rootsizer->Add(vsizer, 1, wxEXPAND);
		//rootsizer->AddSpacer(2);
		SetSizerAndFit(vsizer);
		SetAutoLayout(1);

		CheckFile();

		creating = 0;
		return true;
	}

	// If valid file, updates chain/model, enables
	// Add button.  Otherwise, disables all 3.
	void CheckFile() {
		if (currentPDB) CleanupPDB(currentPDB);
		FindWindow(ID_ALIGNMENT_PATH);

		PDBData *pdb = 0;
		path = file->GetPath();
		if (!wxFileExists(path)) {
			FileRequest *r = GetFileRequest();
			if (r && r->m_localFile) {
				path = r->m_path;
			}
			if (r) delete r;
		}
		pdb = LoadPDBFromString(path);

		currentPDB = pdb;
		if (pdb && !pdb->numModels) {
			CleanupPDB(pdb);
			pdb = 0;
		}
		add->Enable(pdb != 0);
		chain->Enable(pdb != 0);
		model->Enable(pdb != 0);
		if (pdb) {
			model->SetValue(wxString::Format(_T("%i"), pdb->models[0]->number));
			wxString temp = wxEmptyString;
			if (pdb->models[0]->numChains) {
				for (int i=0; i<pdb->models[0]->numChains; i++) {
					temp += pdb->models[0]->chains[i]->chainName;
				}
			}
			chain->SetValue(temp);
		}
		else {
			model->SetValue(wxEmptyString);
			chain->SetValue(wxEmptyString);
		}

		CheckModelAndChain();
		UpdateButtons();
	}

	void CheckModelAndChain() {
		if (!currentPDB) return;
		wxString modelString = model->GetValue();
		long m;
		if (!modelString.ToLong(&m, 10)) {
			if (modelString.Len()) {
				add->Enable(0);
				return;
			}
			m = 0;
		}
		for (int i=0; i<currentPDB->numModels; i++) {
			if (currentPDB->models[i]->number == m) {
				wxString chainString = chain->GetValue();
				PDBModel *pdb = currentPDB->models[i];
				if (!chainString.Len() && pdb->numChains) {
					add->Enable(1);
					return;
				}
				for (int i=0; i<(int)chainString.Len(); i++) {
					for (int j=0; j<pdb->numChains; j++) {
						if (toupper(chainString[i]) == toupper(pdb->chains[j]->chainName)) {
							add->Enable(1);
							return;
						}
					}
				}
			}
		}
		add->Enable(0);
	}

	int AddChain(const wxString &modelString, const wxString &chainString) {
		long m;
		if (!modelString.ToLong(&m, 10)) {
			if (modelString.Len()) {
				return 0;
			}
			m = 0;
		}
		int i;
		for (i=0; i<currentPDB->numModels; i++) {
			if (currentPDB->models[i]->number == m) {
				break;
			}
		}
		if (i == currentPDB->numModels)
			return 0;
		wxString chains;
		PDBModel *pdb = currentPDB->models[i];
		int added = 0;
		if (!chainString.Len() && pdb->numChains) {
			for (int i=0; i<pdb->numChains; i++) {
				if (pdb->numChains == 1 || pdb->chains[i]->chainName != ' ') {
					info->pdb = (PDBInfo**) realloc(info->pdb, (info->numPDB+1) * sizeof(*info->pdb));
					info->pdb[info->numPDB] = new PDBInfo();
					PDBInfo *inf = info->pdb[info->numPDB++];
					inf->modelNum = m;
					inf->name = path;
					inf->chain = pdb->chains[i]->chainName;
					inf->numRes = pdb->chains[i]->length;
					added++;
				}
			}
		}
		else {
			for (int i=0; i<(int)chainString.Len(); i++) {
				int index = -1;
				for (int j=0; j<pdb->numChains; j++) {
					if (pdb->chains[j]->chainName == chainString[i]) {
						index = j;
						break;
					}
					if (toupper(pdb->chains[j]->chainName) == toupper(chainString[i])) {
						index = j;
					}
				}
				if (index >= 0) {
					info->pdb = (PDBInfo**) realloc(info->pdb, (info->numPDB+1) * sizeof(*info->pdb));
					info->pdb[info->numPDB] = new PDBInfo();
					PDBInfo *inf = info->pdb[info->numPDB++];
					inf->modelNum = m;
					inf->name = path;
					inf->chain = pdb->chains[index]->chainName;
					inf->numRes = pdb->chains[index]->length;
					added++;
					CleanupChain(PopChain(pdb, inf->chain));
				}
			}
			CheckFile();
		}
		if (added) {
			for (int i=info->numPDB-added; i<info->numPDB; i++) {
				PDBInfo *inf = info->pdb[i];
				wxString name(currentPDB->name, wxConvUTF8);
				list->InsertItem(i, name);
				list->SetItem(i, 1, wxString::Format(_T("%i"), inf->modelNum));
				list->SetItem(i, 2, wxString::Format(_T("%c"), inf->chain));
				list->SetItem(i, 3, wxString::Format(_T("%i"), inf->numRes));
			}
			UpdateButtons();
		}
		return added;
	}

	FileRequest *GetFileRequest() {
		wxString name = file->GetPath();
		return CreateFileRequest(name, this);
	}

	// Enables/disables align button only.
	void UpdateButtons() {
		align->Enable(list->GetItemCount() > 1 && !numRequests);
		downloadCancel->Enable(numRequests != 0);

		FileRequest *r = GetFileRequest();
		bool fileHappy = 0;
		if (r && r->m_happy && !r->m_localFile) {
			fileHappy = 1;
		}
		if (r) delete r;
		download->Enable(fileHappy);
		downloadAndAdd->Enable(fileHappy);
	}

	void OnFileChange(wxFileDirPickerEvent& evt);
	void OnTextChange(wxCommandEvent& evt);
	void OnButton(wxCommandEvent& evt);

	void DeleteRequest(int i) {
		if (i >= 0 && i < numRequests) {
			delete requests[i];
			if (i == numRequests-1)
				lastRequestIsAdd = 1;
			--numRequests;
			while (i < numRequests) {
				requests[i] = requests[i+1];
				i++;
			}
			if (!numRequests) {
				free(requests);
				requests = 0;
				RequestTimer.Stop();
			}
			// Don't automatically call UpdateButtons().
			// Should be safe, but just being careful
			// when deleting the structure.
		}
	}

	void OnRequestTimer(wxTimerEvent &evt) {
		UpdateRequest();
	}

	void UpdateRequest() {
		int stop = 1;
		if (numRequests) {
			if (requests[0]->Update()) {
				stop = 0;
			}
			else {
				if (requests[0]->m_happy) {
					if (1 != numRequests || lastRequestIsAdd) {
						PDBData *pdb = LoadPDBFromString(requests[0]->m_path);
						if (pdb && (!pdb->foundEnd || pdb->error)) {
							// Partial download.
							delete pdb;
							pdb = 0;
						}
						if (!pdb) {
							wxRemoveFile(requests[0]->m_path);
							wxString status =
								wxString(_T("Problem downloading ")) << requests[0]->m_path << _T(".\nurl:") << requests[0]->m_urlString;
							wxMessageBox(status, _T("Download error"), wxICON_ERROR | wxOK, this);
						}
						else {
							// Both these will be reverted after call to
							// CheckFile();
							path = requests[0]->m_path;
							CleanupPDB(currentPDB);
							currentPDB = pdb;
							AddChain(wxEmptyString, wxEmptyString);
						}
					}
				}
				DeleteRequest(0);
				RequestTimer.Stop();
				if (numRequests) {
					// Start next request.
					requests[0]->Update();
					stop = 0;
				}
				CheckFile();
				// Checkfile does this, too.
				// UpdateButtons();
			}
		}
		if (RequestTimer.IsRunning()) {
			if (stop) RequestTimer.Stop();
		}
		else {
			if (!stop) RequestTimer.Start(200);
		}
	}

	void DeleteSelected() {
		long pos = 0;
		while (1) {
			pos = list->GetNextItem(pos-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
			if (pos < 0) break;
			list->DeleteItem(pos);
			delete info->pdb[pos];
			info->numPDB--;
			int i = pos;
			while (i<info->numPDB) {
				info->pdb[i] = info->pdb[i+1];
				i++;
			}
		}
		UpdateButtons();
	}

	void OnListKeyDown(wxListEvent& evt) {
		int keyCode = evt.GetKeyCode();
		if (keyCode == WXK_DELETE || keyCode == WXK_BACK) {
			DeleteSelected();
		}
		else {
			// Don't want to ignore any default handling.
			evt.Skip();
		}
	}

	void CleanupRequests() {
		while (numRequests) {
			DeleteRequest(numRequests-1);
		}
	}

	void StartRequest() {
		UpdateRequest();
	}


	DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(AlignmentDialog, wxDialog)
	EVT_LIST_KEY_DOWN(ID_ALIGNMENT_CHAIN_LIST, AlignmentDialog::OnListKeyDown)
	EVT_FILEPICKER_CHANGED(wxID_ANY, AlignmentDialog::OnFileChange)
	EVT_TEXT(wxID_ANY, AlignmentDialog::OnTextChange)
	EVT_BUTTON(wxID_ANY, AlignmentDialog::OnButton)
	EVT_TIMER(wxID_ANY, AlignmentDialog::OnRequestTimer)
END_EVENT_TABLE()

void AlignmentDialog::OnFileChange(wxFileDirPickerEvent& evt) {
	CheckFile();
}

void AlignmentDialog::OnTextChange(wxCommandEvent& evt) {
	CheckModelAndChain();
}

void AlignmentDialog::OnButton(wxCommandEvent& evt) {
	switch (evt.GetId()) {
		case wxID_CANCEL:
			EndModal(wxCANCEL);
			break;
		case ID_ALIGNMENT_CLEAR:
			list->DeleteAllItems();
			delete info;
			info = new AlignmentInfo();
			UpdateButtons();
			break;
		case ID_ALIGNMENT_REMOVE_CHAIN:
			DeleteSelected();
			break;
		case ID_ALIGNMENT_ALIGN:
			if (list->GetItemCount() >= 2) {
				int alignmentFlags = 0;
				int bonusFlags = 0;
				if (cbNewWindow && cbNewWindow->GetValue()) {
					alignmentFlags |= ALIGNMENT_NEW_WINDOW;
				}
				else {
					bonusFlags |= config->alignmentFlags & ALIGNMENT_NEW_WINDOW;
				}
				if (rbMultipleWindows->GetValue()) {
					alignmentFlags |= ALIGNMENT_MULTIPLE_WINDOWS;
				}
				else if (rbMultipleFrames->GetValue()) {
					alignmentFlags |= ALIGNMENT_MULTIPLE_FRAMES;
				}
				info->flags = alignmentFlags;
				config->alignmentFlags = alignmentFlags | bonusFlags;
				config->Save();
				EndModal(wxOK);
			}
			break;
		case ID_ALIGNMENT_ADD_CHAIN:
			AddChain(model->GetValue(), chain->GetValue());
			file->SetFocus();
			break;
		case ID_ALIGNMENT_DOWNLOAD_CANCEL:
			CleanupRequests();
			UpdateButtons();
			break;
		case ID_ALIGNMENT_DOWNLOAD:
		case ID_ALIGNMENT_DOWNLOAD_AND_ADD:
			{
				if (!lastRequestIsAdd) break;
				FileRequest *r = GetFileRequest();
				if (!r) break;
				int happy = 1;
				if (!r->m_happy || r->m_localFile) {
					//???
					happy = 0;
				}
				else {
					for (int i=0; i<numRequests; i++) {
						if (!r->m_path.CmpNoCase(requests[i]->m_path)) {
							happy = 0;
							wxMessageBox(_T("File already queued for download"), _T("Error"), wxOK | wxICON_ERROR, this);
							break;
						}
					}
				}
				if (!happy) {
					delete r;
					UpdateButtons();
					break;
				}

				if (evt.GetId() == ID_ALIGNMENT_DOWNLOAD)
					lastRequestIsAdd = 0;
				requests = (FileRequest**) realloc(requests, (numRequests+1) * sizeof(FileRequest*));
				requests[numRequests++] = r;
				if (numRequests == 1) {
					StartRequest();
				}
				UpdateButtons();
				break;
			}
	}
}

/*
enum AlignmentDialogId {
	ID_ALIGNMENT_PATH,
	ID_ALIGNMENT_CHAIN,
	ID_ALIGNMENT_MODEL,

	ID_ALIGNMENT_ADD_CHAIN,

	ID_ALIGNMENT_CHAIN_LIST,

	ID_ALIGNMENT_REMOVE_CHAIN,
	ID_ALIGNMENT_CLEAR,
	ID_ALIGNMENT_ALIGN,
	ID_ALIGNMENT_CANCEL,
};//*/

AlignmentInfo *MultipleAlignmentDialog(wxWindow *parent, const wxString &path, int forceNoNewWindow) {
	AlignmentDialog dialog(path);

	if (dialog.Create(parent, forceNoNewWindow) && dialog.ShowModal() == wxOK) {
		AlignmentInfo *out = dialog.info;
		dialog.info = 0;
		return out;
	}

	return 0;
}
